﻿namespace System.Threading.Tasks.Dataflow
{
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.Text;

	public abstract class TargetBlockSetBase : ITargetBlock<object>
	{
		private TaskCompletionSource<object> _tcs =
			new TaskCompletionSource<object>();

		/// <summary>
		/// Gets a <see cref="T:System.Threading.Tasks.Task">Task</see> that represents the asynchronous operation and completion of the dataflow block.
		/// </summary>
		/// <value></value>
		/// <remarks>
		/// A dataflow block is considered completed when it is not currently processing a message and when it has guaranteed that it will not process
		/// any more messages. The returned <see cref="T:System.Threading.Tasks.Task">Task</see> will transition to a completed state when the
		/// associated block has completed. It will transition to the <see cref="T:System.Threading.Tasks.TaskStatus">RanToCompletion</see> state
		/// when the block completes its processing successfully according to the dataflow block’s defined semantics, it will transition to
		/// the <see cref="T:System.Threading.Tasks.TaskStatus">Faulted</see> state when the dataflow block has completed processing prematurely due to an unhandled exception,
		/// and it will transition to the <see cref="T:System.Threading.Tasks.TaskStatus">Canceled</see> state when the dataflow  block has completed processing
		/// prematurely due to receiving a cancellation request.  If the task completes in the Faulted state, its Exception property will return
		/// an <see cref="T:System.AggregateException"/> containing the one or more exceptions that caused the block to fail.
		/// </remarks>
		public Task CompletionTask
		{
			get
			{
				return _tcs.Task;
			}
		}

		/// <summary>
		/// Gets a <see cref="T:System.Threading.Tasks.Task">Task</see> that represents the asynchronous operation and completion of the dataflow block.
		/// </summary>
		/// <value></value>
		/// <remarks>
		/// A dataflow block is considered completed when it is not currently processing a message and when it has guaranteed that it will not process
		/// any more messages. The returned <see cref="T:System.Threading.Tasks.Task">Task</see> will transition to a completed state when the
		/// associated block has completed. It will transition to the <see cref="T:System.Threading.Tasks.TaskStatus">RanToCompletion</see> state
		/// when the block completes its processing successfully according to the dataflow block’s defined semantics, it will transition to
		/// the <see cref="T:System.Threading.Tasks.TaskStatus">Faulted</see> state when the dataflow block has completed processing prematurely due to an unhandled exception,
		/// and it will transition to the <see cref="T:System.Threading.Tasks.TaskStatus">Canceled</see> state when the dataflow  block has completed processing
		/// prematurely due to receiving a cancellation request.  If the task completes in the Faulted state, its Exception property will return
		/// an <see cref="T:System.AggregateException"/> containing the one or more exceptions that caused the block to fail.
		/// </remarks>
		public Task Completion
		{
			get
			{
				return _tcs.Task;
			}
		}

		public virtual void Fault(Exception exception)
		{
			_tcs.TrySetException(exception);
		}

		public virtual void Complete()
		{
			_tcs.TrySetResult(null);
		}

		public abstract DataflowMessageStatus OfferMessage(
			DataflowMessageHeader messageHeader, 
			object messageValue, 
			ISourceBlock<object> source, 
			bool consumeToAccept);
	}

	public class TargetBlockSet<T0, T1> : TargetBlockSetBase
	{
		private ITargetBlock<T0> _t0;
		private ITargetBlock<T1> _t1;

		public TargetBlockSet(
			ITargetBlock<T0> t0,
			ITargetBlock<T1> t1)
		{
			_t0 = t0;
			_t1 = t1;
		}

		public override void Complete()
		{
			_t0.Complete();
			_t1.Complete();
			base.Complete();
		}

		public override DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, object messageValue, ISourceBlock<object> source, bool consumeToAccept)
		{
			if (typeof(T0).IsInstanceOfType(messageValue))
			{
				return _t0.OfferMessage(
					messageHeader,
					(T0)messageValue,
					(ISourceBlock<T0>)source,
					consumeToAccept);
			}
			if (typeof(T1).IsInstanceOfType(messageValue))
			{
				return _t1.OfferMessage(
					messageHeader,
					(T1)messageValue,
					(ISourceBlock<T1>)source,
					consumeToAccept);
			}
			return DataflowMessageStatus.Declined;
		}
	}

	public class TargetBlockSet<T0, T1, T2> : TargetBlockSetBase
	{
		private ITargetBlock<T0> _t0;
		private ITargetBlock<T1> _t1;
		private ITargetBlock<T2> _t2;

		public TargetBlockSet(
			ITargetBlock<T0> t0,
			ITargetBlock<T1> t1,
			ITargetBlock<T2> t2)
		{
			_t0 = t0;
			_t1 = t1;
			_t2 = t2;
		}

		public override void Complete()
		{
			_t0.Complete();
			_t1.Complete();
			_t2.Complete();
			base.Complete();
		}

		public override DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, object messageValue, ISourceBlock<object> source, bool consumeToAccept)
		{
			if (typeof(T0).IsInstanceOfType(messageValue))
			{
				return _t0.OfferMessage(
					messageHeader,
					(T0)messageValue,
					(ISourceBlock<T0>)source,
					consumeToAccept);
			}
			if (typeof(T1).IsInstanceOfType(messageValue))
			{
				return _t1.OfferMessage(
					messageHeader,
					(T1)messageValue,
					(ISourceBlock<T1>)source,
					consumeToAccept);
			}
			if (typeof(T2).IsInstanceOfType(messageValue))
			{
				return _t2.OfferMessage(
					messageHeader,
					(T2)messageValue,
					(ISourceBlock<T2>)source,
					consumeToAccept);
			}
			return DataflowMessageStatus.Declined;
		}
	}

	public class TargetBlockSet<T0, T1, T2, T3> : TargetBlockSetBase
	{
		private ITargetBlock<T0> _t0;
		private ITargetBlock<T1> _t1;
		private ITargetBlock<T2> _t2;
		private ITargetBlock<T3> _t3;

		public TargetBlockSet(
			ITargetBlock<T0> t0,
			ITargetBlock<T1> t1,
			ITargetBlock<T2> t2,
			ITargetBlock<T3> t3)
		{
			_t0 = t0;
			_t1 = t1;
			_t2 = t2;
			_t3 = t3;
		}

		public override void Complete()
		{
			_t0.Complete();
			_t1.Complete();
			_t2.Complete();
			_t3.Complete();
			base.Complete();
		}

		public override DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, object messageValue, ISourceBlock<object> source, bool consumeToAccept)
		{
			if (typeof(T0).IsInstanceOfType(messageValue))
			{
				return _t0.OfferMessage(
					messageHeader,
					(T0)messageValue,
					(ISourceBlock<T0>)source,
					consumeToAccept);
			}
			if (typeof(T1).IsInstanceOfType(messageValue))
			{
				return _t1.OfferMessage(
					messageHeader,
					(T1)messageValue,
					(ISourceBlock<T1>)source,
					consumeToAccept);
			}
			if (typeof(T2).IsInstanceOfType(messageValue))
			{
				return _t2.OfferMessage(
					messageHeader,
					(T2)messageValue,
					(ISourceBlock<T2>)source,
					consumeToAccept);
			}
			if (typeof(T3).IsInstanceOfType(messageValue))
			{
				return _t3.OfferMessage(
					messageHeader,
					(T3)messageValue,
					(ISourceBlock<T3>)source,
					consumeToAccept);
			}
			return DataflowMessageStatus.Declined;
		}
	}
}
